package api

import (
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/mitchellh/mapstructure"
	"github.com/rs/zerolog/log"

	"github.com/fakeyanss/jt808-server-go/internal/config"
	"github.com/fakeyanss/jt808-server-go/internal/iot"
	"github.com/fakeyanss/jt808-server-go/internal/protocol/model"
	"github.com/fakeyanss/jt808-server-go/internal/server"
	"github.com/fakeyanss/jt808-server-go/internal/storage"
	log2 "github.com/fakeyanss/jt808-server-go/pkg/log"
)

func Run(serv *server.TCPServer, cfg *config.Config) {
	// web server structure
	gin.SetMode(gin.ReleaseMode)
	router := gin.Default()
	cache := storage.GetDeviceCache()
	geoCache := storage.GetGeoCache()

	router.GET("/location", func(c *gin.Context) {
		sn := c.Query("sn")
		start := c.Query("start")
		end := c.Query("end")
		list, err := iot.GlobalDeviceManager.ListLocation(sn, start, end)
		if err != nil {
			log2.Error("http location",
				log2.Any("err", err),
				log2.String("sn", sn),
				log2.String("start", start),
				log2.String("end", end),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"err": err.Error()})
			return
		}

		c.JSON(http.StatusOK, list)
	})

	router.GET("/last", func(c *gin.Context) {
		sns := c.Query("sn")

		list := strings.Split(sns, ",")

		data, err := iot.GlobalDeviceManager.FindLastBySNList(list)
		if err != nil {
			log2.Error("http last",
				log2.Any("err", err),
				log2.String("sn", sns),
			)
			c.JSON(http.StatusInternalServerError, gin.H{"err": err.Error()})
			return
		}

		c.JSON(http.StatusOK, data)
	})

	router.GET("/device", func(c *gin.Context) {
		c.JSON(http.StatusOK, cache.ListDevice())
	})

	router.GET("/device/:phone/geo", func(c *gin.Context) {
		phone := c.Param("phone")

		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}

		res := make(map[string]any)
		err = mapstructure.Decode(device, &res)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}
		gis, err := geoCache.GetGeoLatestByPhone(phone)
		if err != nil {
			return
		}
		res["gis"] = gis

		c.JSON(http.StatusOK, res)
	})

	router.GET("/device/:phone/params", func(c *gin.Context) {
		phone := c.Param("phone")
		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}
		session, err := storage.GetSession(device.SessionID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		header := model.GenMsgHeader(device, 0x8104, session.GetNextSerialNum())
		msg := model.Msg8104{
			Header: header,
		}
		serv.Send(session.ID, &msg)
		// todo: read channel from process 0104 msg
	})

	router.PUT("/device/:phone/params", func(c *gin.Context) {
		phone := c.Param("phone")
		params := model.DeviceParams{}
		if err := c.ShouldBind(&params); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}
		session, err := storage.GetSession(device.SessionID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		header := model.GenMsgHeader(device, 0x8103, session.GetNextSerialNum())
		msg := model.Msg8103{
			Header:     header,
			Parameters: &params,
		}
		serv.Send(session.ID, &msg)
	})

	router.PUT("/device/:phone/stream", func(c *gin.Context) {
		phone := c.Param("phone")
		params := model.Msg9101Param{}
		if err := c.ShouldBind(&params); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}
		session, err := storage.GetSession(device.SessionID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		header := model.GenMsgHeader(device, 0x9101, session.GetNextSerialNum())
		msg := model.Msg9101{
			Header:       header,
			Msg9101Param: params,
		}
		serv.Send(session.ID, &msg)
	})

	router.PUT("/device/:phone/rate", func(c *gin.Context) {
		phone := c.Param("phone")
		params := model.Msg9105Param{}
		if err := c.ShouldBind(&params); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}
		session, err := storage.GetSession(device.SessionID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		header := model.GenMsgHeader(device, 0x9105, session.GetNextSerialNum())
		msg := model.Msg9105{
			Header:       header,
			Msg9105Param: params,
		}
		serv.Send(session.ID, &msg)
	})

	router.PUT("/device/:phone/tts", func(c *gin.Context) {
		phone := c.Param("phone")
		// content最大1024字节
		params := model.Msg8300Param{}
		if err := c.ShouldBind(&params); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}
		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
			return
		}
		session, err := storage.GetSession(device.SessionID)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
		}

		header := model.GenMsgHeader(device, 0x8300, session.GetNextSerialNum())
		params.Flag = byte(0xFF)
		msg := model.Msg8300{
			Header:       header,
			Msg8300Param: params,
		}
		serv.Send(session.ID, &msg)
	})

	router.POST("/media/hook/on_stream_none_reader", func(c *gin.Context) {
		req := model.MediaServerHookNoneReader{}
		if err := c.ShouldBind(&req); err != nil {
			log.Error().Msgf("media hook none_reader msg error, %v", err)
			return
		}

		// mediaServerId: your_server_id, app: live, schema: hls, stream: 018913134096-4, vhost: __defaultVhost__
		log.Debug().Msgf("media server hook none_reader, %s", req.String())

		resp := model.MediaServerHookNoneReaderResp{
			Close: true,
		}
		c.JSON(http.StatusOK, resp)

		// 通知设备关闭推流
		split := strings.Split(req.Stream, "-")
		if len(split) != 2 {
			log.Error().Msgf("stream unknown format, %s", req.Stream)
			return
		}
		phone, channelNo := split[0], split[1]
		channelNoV, err := strconv.ParseUint(channelNo, 10, 8)
		if err != nil {
			log.Error().Msgf("failed to parse channel no %s, err %v", channelNo, err)
			return
		}

		device, err := cache.GetDeviceByPhone(phone)
		if err != nil {
			// 设备不在线
			log.Error().Msgf("hook on_stream_none_reader, found no device by phone %s", phone)
			return
		}
		session, err := storage.GetSession(device.SessionID)
		if err != nil {
			// 设备不在线
			log.Error().Msgf("hook on_stream_none_reader, found no session by phone %s sessionID", phone, device.SessionID)
			return
		}

		header := model.GenMsgHeader(device, 0x9102, session.GetNextSerialNum())
		p := model.Msg9102Param{
			ChannelNo:   uint8(channelNoV),
			Command:     0, // 关闭音视频传输
			CommandType: 0, // 关闭音视频数据
			FlowType:    0, // whatever
		}
		msg9102 := model.Msg9102{
			Header:       header,
			Msg9102Param: p,
		}

		// 发送指令
		serv.Send(session.ID, &msg9102)
	})

	httpAddr := ":" + cfg.Server.Port.HTTPPort

	log.Debug().Msgf("Listening and serving HTTP on :%s", cfg.Server.Port.HTTPPort)
	err := router.Run(httpAddr)
	if err != nil {
		log.Error().Err(err).Str("addr", httpAddr).Msg("Fail to run gin router")
		os.Exit(1)
	}
}
